use blk1010_lib::{
    fill::{BlockFillMaster, CalcState},
    map::Map,
};
use wasm_bindgen::prelude::*;
use web_sys::js_sys::{self};

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
    #[wasm_bindgen(js_namespace = console)]
    fn error(s: &str);
}

#[wasm_bindgen]
pub fn init_hook() {
    console_error_panic_hook::set_once();
}

#[wasm_bindgen]
pub struct BlkHelper {
    fill_master: *mut BlockFillMaster,
    fill_map: *mut Map,
    on_fill_finish: js_sys::Function,
}

#[wasm_bindgen]
impl BlkHelper {
    #[wasm_bindgen(constructor)]
    pub fn new(task_per_frame: u8, row: u8, col: u8) -> BlkHelper {
        let f = BlockFillMaster::new(task_per_frame);
        let fill = Box::into_raw(Box::new(f));
        let a = Map::new(row, col);
        let map = Box::into_raw(Box::new(a));
        BlkHelper {
            fill_master: fill,
            fill_map: map,
            on_fill_finish: js_sys::Function::default(),
        }
    }
    #[wasm_bindgen(js_name = setScoreDiff)]
    pub fn set_score_diff(&mut self, arr: js_sys::Int32Array) {
        let diff = arr.to_vec();
        let mut fillman = unsafe { Box::from_raw(self.fill_master) };
        fillman.set_score_diff(diff);
        std::mem::forget(fillman);
    }
    #[wasm_bindgen(js_name = print)]
    pub fn print(&self) {
        log("BlkHelper ==> Hello Rust~");
    }
    #[wasm_bindgen(js_name = add)]
    pub fn add(&self, a: i32, b: i32) -> i32 {
        a + b
    }
    #[wasm_bindgen(js_name = fillAsync)]
    pub fn fill_async(
        &mut self,
        map_array: js_sys::Uint8Array,
        combo: u8,
        score: u32,
        collection: u32,
        hard_level: i8,
        hard_type: u8,
        cb: js_sys::Function,
        _delta_time: f32,
        clear_all_score: u32,
    ) {
        let uint8_slice = map_array.to_vec();
        let mut data = [0u8; 64];
        for (i, value) in uint8_slice.iter().enumerate() {
            data[i] = *value;
        }
        self.on_fill_finish = cb;
        let mut fillman = unsafe { Box::from_raw(self.fill_master) };
        let mut map = unsafe { Box::from_raw(self.fill_map) };

        map.set_grid(&data);
        map.combo = combo;
        map.score = score;
        map.hard_type = hard_type;
        map.collection = collection;
        map.hard_level = hard_level;
        map.clear_all_score = clear_all_score;

        fillman.fill_reset();
        fillman.fill_request(&mut map);
        // log(format!("score:{} clear_all_score:{} fill_info:{}", score, clear_all_score,fillman.msg_debug).as_str());
        std::mem::forget(fillman);
        std::mem::forget(map);
    }
    #[wasm_bindgen(js_name = fillAsyncRevive)]
    pub fn fill_async_revive(
        &mut self,
        map_array: js_sys::Uint8Array,
        combo: u8,
        score: u32,
        cb: js_sys::Function,
        _delta_time: f32,
        clear_all_score: u32,
    ) {
        let uint8_slice = map_array.to_vec();
        let mut data = [0u8; 64];
        for (i, value) in uint8_slice.iter().enumerate() {
            data[i] = *value;
        }
        self.on_fill_finish = cb;
        let mut fillman = unsafe { Box::from_raw(self.fill_master) };
        let mut map = unsafe { Box::from_raw(self.fill_map) };

        map.set_grid(&data);
        map.combo = combo;
        map.score = score;
        map.clear_all_score = clear_all_score;

        fillman.fill_reset();
        fillman.fill_revive(&mut map);
        // log(format!("score:{} clear_all_score:{} fill_info:{}", score, clear_all_score,fillman.msg_debug).as_str());
        std::mem::forget(fillman);
        std::mem::forget(map);
    }
    #[wasm_bindgen(js_name = fillUpdate)]
    pub fn fill_update(&mut self) {
        let mut fillman = unsafe { Box::from_raw(self.fill_master) };
        fillman.update();
        if fillman.cstate == CalcState::FillOk {
            let js_result = js_sys::Uint8Array::from(fillman.ret.as_slice()).buffer();
            let allc = js_sys::Uint8Array::from(fillman.ret_all_clear.as_slice()).buffer();
            let mutil = js_sys::Uint8Array::from(fillman.ret_multi_clear.as_slice()).buffer();
            self.on_fill_finish
                .call3(&JsValue::null(), &js_result, &allc, &mutil)
                .unwrap();
        }
        std::mem::forget(fillman);
    }

    #[wasm_bindgen(js_name = fillFree)]
    pub fn fill_free(&self) {
        if !self.fill_map.is_null() {
            unsafe {
                let _ = Box::from_raw(self.fill_map);
            };
        }
        if !self.fill_master.is_null() {
            unsafe {
                let _ = Box::from_raw(self.fill_master);
            };
        }
    }
}
